package com.zyncro.test.multipart.httpclient;

import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import com.zyncro.test.multipart.MultipartRequest;
import com.zyncro.test.multipart.Response;

public class HttpClientMultipartRequest implements MultipartRequest {

	private static final HttpClient httpClient = new DefaultHttpClient();

	private final HttpEntityEnclosingRequestBase request;
	private final List<NameValuePair> qparams = new ArrayList<NameValuePair>();

	private final String verb;
	private final String url;

	private final Map<String, String> stringBodyParams = new HashMap<String, String>();
	private final Map<String, InputStreamBody> streamBodyParams = new HashMap<String, InputStreamBody>();

	public HttpClientMultipartRequest(String verb, String url) {

		if (verb == null || url == null) {
			throw new IllegalArgumentException("Verb and url cannot be null");
		}

		if (!verb.equals("POST") && !verb.equals("PUT")) {
			throw new IllegalArgumentException(String.format("Method %s multipart not supported", verb));
		}

		this.verb = verb;
		this.request = verb.equals("POST") ? new HttpPost() : new HttpPut();

		this.url = url;
	}

	@Override
	public void addQuerystringParameter(String key, String value) {
		qparams.add(new BasicNameValuePair(key, value));
	}

	@Override
	public void addHeader(String key, String value) {
		request.addHeader(key, value);
	}

	@Override
	public void addBodyParameter(String key, String value) {
		stringBodyParams.put(key, value);
	}

	@Override
	public void addStreamBodyParameter(String key, InputStream value) {
		InputStreamBody uploadFilePart = new InputStreamBody(value, "file");
		streamBodyParams.put(key, uploadFilePart);
	}

	@Override
	public String getVerb() {
		return verb;
	}

	@Override
	public String getUrl() {
		return url;
	}

	@Override
	public Map<String, String> getHeaders() {
		Map<String, String> headers = new HashMap<String, String>();
		for (Header header : request.getAllHeaders()) {
			headers.put(header.getName(), header.getValue());
		}
		return headers;
	}

	@Override
	public Map<String, String> getQueryStringParams() {
		Map<String, String> queryParams = new HashMap<String, String>();
		for (NameValuePair nameValuePair : qparams) {
			queryParams.put(nameValuePair.getName(), nameValuePair.getValue());
		}
		return queryParams;
	}

	@Override
	public Map<String, String> getBodyParams() {
		return new HashMap<String, String>(stringBodyParams);
	}

	@Override
	public Map<String, InputStream> getStreamBodyParams() {
		Map<String, InputStream> params = new HashMap<String, InputStream>();

		for (Map.Entry<String, InputStreamBody> streamParam : streamBodyParams.entrySet()) {
			params.put(streamParam.getKey(), streamParam.getValue().getInputStream());
		}
		return params;
	}

	@Override
	public Response send() {

		getHttpClientRequest();

		try {
			HttpResponse response = httpClient.execute(request);
			return new HttpClientResponse(response);

		} catch (Exception e) {
			throw new IllegalStateException("Problems sending multipart request", e);
		}
	}

	public HttpEntityEnclosingRequestBase getHttpClientRequest() {

		request.setURI(this.getURI());

		MultipartEntity reqEntity = this.getMultipartEntity();
		request.setEntity(reqEntity);

		return request;
	}

	public URI getURI() {
		return URI.create(url + "?" + URLEncodedUtils.format(qparams, "UTF-8"));
	}

	public MultipartEntity getMultipartEntity() {
		MultipartEntity reqEntity = new MultipartEntity();

		try {
			for (Map.Entry<String, String> stringParam : stringBodyParams.entrySet()) {
				StringBody str = new StringBody(stringParam.getValue());
				reqEntity.addPart(stringParam.getKey(), str);
			}

			for (Map.Entry<String, InputStreamBody> streamParam : streamBodyParams.entrySet()) {
				reqEntity.addPart(streamParam.getKey(), streamParam.getValue());
			}
		} catch (Exception e) {
			throw new IllegalStateException("Problems creating multipart request entity", e);
		}
		return reqEntity;
	}

	public void addStreamBodyParameter(String key, InputStream value, String mimeType) {
		InputStreamBody uploadFilePart = new InputStreamBody(value, mimeType, "file");
		streamBodyParams.put(key, uploadFilePart);
	}

	public static HttpClient getHttpClient() {
		return httpClient;
	}
}
